/**
 * #info
 *@author   lulg
 *@createTime   2018/03/20
 *@updateTime    2018/05/04
 *@description   tabs  component 页签导航
 */

/**
 * #item
 *
 * @type        aty-tabs
 * @cn          页签导航
 * @menu        导航
 * @order       15
 * @stPlugin    true
 * @stContainer false
 * @children    aty-tabpane,aty-tabspace,template,aty-button,aty-link,aty-panel
 */

// 扩展属性
/**
 * #extendproperty
 *
 * @name    slot
 * @cn      额外内容
 * @type    list
 * @options [extra]
 * @value   extra
 * @description  aty-tabs插槽，<aty-button slot="extra">增加页签</aty-button>
 */
import config from '../../../config/config.js'

// components
import Icon from '../../basic/icon/index.vue'
import tab from '../../navigation/tabs/tab/tab.js'
import Emitter from '../../../mixins/emitter.js'
import elementResizeDetectorMaker from 'element-resize-detector'
//  prefix
const prefixCls = config.prefix + '-tabs'
// module
const module = {
  name: prefixCls,
  components: {
    Icon, tab
  },
  mixins: [Emitter],
  props: {
    /**
     * #property
     *
     * @name    value
     * @cn      页签值
     * @type    string,number
     * @description 用于标识选中的页签，对应选中aty-tabpane的name值或index,可以使用v-model进行双向绑定
     */
    value: {
      type: [String, Number]
    },
    /**
     * #property
     *
     * @name    type
     * @cn      页签类型
     * @type    list
     * @options [line, card]
     * @value   line
     * @description 页签类型，line 线型， card 卡片型, trapezoid 梯形
     */
    type: {
      validator (value) {
        return Artery.isOneOf(value, ['line', 'card', 'trapezoid'])
      },
      default: 'line'
    },
    /**
     * #property
     *
     * @name    animated
     * @cn      启用动画效果
     * @type    boolean
     * @value   true
     * @bind    must
     * @description 是否启用动画效果，默认启用
     */
    animated: {
      type: Boolean,
      default: true
    },
    /**
     * #property
     *
     * @name    closable
     * @cn      可关闭
     * @type    boolean
     * @bind    must
     * @value   false
     * @description 是否启用关闭，默认启用
     */
    closable: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    shareframe
     * @cn      共享iframe
     * @type    boolean
     * @bind    must
     * @description 是否共享iframe,启用后需在每个aty-pane需要有src属性值
     */
    shareframe: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    position
     * @cn      页签位置
     * @type    list
     * @value   top
     * @options [top,bottom,left, right]
     * @description 页签的位置，取值为top，bottom，left，right
     */
    position: {
      type: String,
      default: 'top'
    },
    /**
     * #property
     *
     * @name    tabAnimation
     * @cn      tab切换动画
     * @type    boolean
     * @bind    must
     * @value   true
     * @description 是否展示切换动画
     */
    tabAnimation: {
      type: Boolean,
      default: true
    }
  },
  watch: {
    value (val) {
      this.activeKey = val
    },
    activeKey () {
      this.updateBar()
      this.updateStatus()
      // 折行代码不知道干嘛用，跟Table有交互
      this.broadcast('Table', 'on-visible-change', true)
      this.$nextTick(() => {
        this.scrollToActiveTab()
      })
    }
  },
  data () {
    return {
      prefixCls: prefixCls,
      navList: [],
      barWidth: 0,
      barHeight: 0,
      barOffset: 0,
      activeKey: this.value,
      showSlot: false,
      navStyle: {
        transform: ''
      },
      scrollable: false,
      scope: {
        className: config.prefix + '-tabs'
      },
      currentSrc: '',
      status: 'center' // left-full, right-full, center
      /*
       * tab动画: 当指定为true, 并且不是IE, 则显示动画
       * currentTabAnimation: this.tabAnimation && !(navigator.userAgent.indexOf('Trident') !== -1 || navigator.userAgent.indexOf('MSIE') !== -1)
       */
    }
  },
  computed: {
    currentanimated () {
      return this.shareframe ? false : this.animated
    },
    classes () {
      return [
                `${prefixCls}`,
                {
                  [`${prefixCls}-card`]: this.type === 'card',
                  [`${prefixCls}-trapezoid`]: this.type === 'trapezoid',
                  [`${prefixCls}-no-animation`]: !this.currentanimated
                }
      ]
    },
    contentClasses () {
      return [
                `${prefixCls}-content`,
                {
                  [`${prefixCls}-content-animated`]: this.currentanimated
                },
                `${prefixCls}-${this.position}`
      ]
    },
    barContainnerClasses () {
      return [
                `${prefixCls}-bar`,
                `${prefixCls}-${this.position}`
      ]
    },
    barClasses () {
      return [
                `${prefixCls}-ink-bar`,
                {
                  [`${prefixCls}-ink-bar-animated`]: this.currentanimated
                }
      ]
    },
    contentStyle () {
      if (this.tabAnimation) {
        const x = this.navList.findIndex((nav) => nav.name === this.activeKey)
        const p = x === 0 ? '0%' : `-${x}00%`

        let style = {}
        if (x > -1) {
          style = {
            transform: `translateX(${p}) translateZ(0px)`
          }
        }
        return style
      } else {
        return {}
      }
    },
    barStyle () {
      const style = {
        display: 'none'
      }
      if (this.type === 'card') {
        return style
      }
      if (this.type === 'line') style.display = 'block'
      if (this.currentanimated) {
        if (this.barWidth !== 0) {
          style.width = `${this.barWidth}px`
          style.transform = `translate3d(${this.barOffset}px, 0px, 0px)`
        } else {
          style.height = `${this.barHeight}px`
          style.transform = `translate3d(0px, ${this.barOffset}px, 0px)`
        }
      } else {
        style.left = `${this.barOffset}px`
      }

      return style
    }
  },
  methods: {
    filterTabs (children) {
      return children.filter(item => item.$options.name === config.prefix + '-tabs-tabpane' || item.$options.name === config.prefix + '-tabs-tabspace')
    },
    getTabs () {
      let all = this.filterTabs(this.$children)
      this.$children.filter(item => item.$options.name !== config.prefix + '-tabs-tabpane' && item.$options.name !== config.prefix + '-tabs-tabspace')
        .map(item => {
          all = all.concat(this.filterTabs(item.$children))
        })
      return all
    },
    updateNav () {
      this.navList = []
      this.getTabs().forEach((pane, index) => {
        const item = {
          labelType: typeof pane.label,
          label: pane.label,
          icon: pane.icon || '',
          name: pane.currentName || index,
          disabled: pane.disabled,
          closable: pane.closable,
          index: index,
          labelVm: pane.$slots.label,
          isSpace: pane.isSpace
        }

        this.navList.push(item)
        if (!pane.currentName) pane.currentName = index
        if (index === 0) {
          if (!this.activeKey) this.activeKey = pane.currentName || index
        }
      })
      this.updateStatus()
      this.updateBar()
    },
    updateBar () {
      this.$nextTick(() => {
        const index = this.navList.findIndex((nav) => nav.name === this.activeKey)
        if (!this.$refs.nav) return // 页面销毁时，这里会报错，为了解决 #2100
        // 更新tab宽度
        const prevTabs = this.$refs.nav.querySelectorAll(`.${prefixCls}-tab`)
        const tab = prevTabs[index]

        if (this.position === 'bottom' || this.position == 'top') {
          this.barWidth = tab ? parseFloat(tab.offsetWidth) : 0

          if (index > 0) {
            let offset = 0
            const gutter = 16
            for (let i = 0; i < index; i++) {
              offset += parseFloat(prevTabs[i].offsetWidth) + gutter
            }

            this.barOffset = offset
          } else {
            this.barOffset = 0
          }
        } else {
          this.barHeight = tab ? parseFloat(tab.offsetHeight) : 0

          if (index > 0) {
            let offset = 0
            for (let i = 0; i < index; i++) {
              offset += parseFloat(prevTabs[i].offsetHeight)
            }

            this.barOffset = offset
          } else {
            this.barOffset = 0
          }
        }
        if (!this.tabAnimation || document.body.style.textShadow === undefined) { // IE9，项目组变态的需求需要用到 http://code.thunisoft.com/artery/artery6/issues/92
          // 此处没有实用数据驱动，因为只有IE9会走，所以直接操作dom改css了
          const children = this.$el.querySelector(`.${prefixCls}-content`).children
          for (let i = 0; i < children.length; i++) {
            if (i === index) {
              children[i].style.display = ''
            } else {
              children[i].style.display = 'none'
            }
          }
        } else {
          this.updateNavScroll()
        }
      })
    },
    updateStatus () {
      const tabs = this.getTabs()
      tabs.forEach(tab => tab.show = (tab.currentName === this.activeKey) || this.currentanimated)
    },
    tabCls (item) {
      return [
                `${prefixCls}-tab`,
                {
                  [`${prefixCls}-tab-disabled`]: item.disabled,
                  [`${prefixCls}-tab-active`]: item.name === this.activeKey
                }
      ]
    },
    handleChange (index) {
      const nav = this.navList[index]
      if (nav.disabled) return
      this.activeKey = nav.name
      if (this.shareframe) {
        const tabs = this.getTabs()
        this.currentSrc = tabs[index].src
      }
      this.$emit('input', nav.name)
      /**
       * #event
       *
       * @name    tabclick
       * @param   value  当前选中的页签的name值
       * @description 页签的点击事件
       * @body
       */
      this.$emit('tabclick', nav.name)
    },
    handleRemove (index) {
      const tabs = this.getTabs()
      const tab = tabs[index]
      tab.$destroy()

      if (tab.currentName === this.activeKey) {
        const newTabs = this.getTabs()
        let activeKey = -1

        if (newTabs.length) {
          const leftNoDisabledTabs = tabs.filter((item, itemIndex) => !item.disabled && itemIndex < index)
          const rightNoDisabledTabs = tabs.filter((item, itemIndex) => !item.disabled && itemIndex > index)

          if (rightNoDisabledTabs.length) {
            activeKey = rightNoDisabledTabs[0].currentName
          } else if (leftNoDisabledTabs.length) {
            activeKey = leftNoDisabledTabs[leftNoDisabledTabs.length - 1].currentName
          } else {
            activeKey = newTabs[0].currentName
          }
        }
        this.activeKey = activeKey
        this.$emit('input', activeKey)
      }
      /**
       * #event
       *
       * @name    tabremove
       * @param   value  被删除的页签值
       * @body    if(value === 1) {this.showTab1 = false;}
       * @description  删除页签触发事件
       */
      this.$emit('tabremove', tab.currentName)
      this.updateNav()
    },
    showClose (item) {
      // if (this.type === 'card') {
      if (item.closable !== null) {
        return item.closable
      } else {
        return this.closable
      }
      /*
       * } else {
       * return false;
       *}
       */
    },
    scrollPrev () {
      const containerWidth = this.$refs.navScroll.offsetWidth
      const currentOffset = this.getCurrentScrollOffset()

      if (!currentOffset) return
      // 原来的计算方式不合理，你无法分辨切换的中间是否还有别的元素
      const tabsList = this.$refs.nav.querySelectorAll('.aty-tabs-tab')
      let widthMin = 0
      for (let i = 0; i < tabsList.length; i++) {
        let tab = tabsList[i]
        widthMin = tab.offsetWidth > widthMin ? tab.offsetWidth : widthMin
      }

      const newOffset = currentOffset > containerWidth
        ? currentOffset - containerWidth + widthMin
        : 0

      this.setOffset(newOffset)
    },
    scrollNext () {
      const navWidth = this.$refs.nav.offsetWidth
      const containerWidth = this.$refs.navScroll.offsetWidth
      const currentOffset = this.getCurrentScrollOffset()
      if (navWidth - currentOffset <= containerWidth) return

      // 原来的计算方式不合理，你无法分辨切换的中间是否还有别的元素
      const tabsList = this.$refs.nav.querySelectorAll('.aty-tabs-tab')
      let widthMin = 0
      for (let i = 0; i < tabsList.length; i++) {
        let tab = tabsList[i]
        widthMin = tab.offsetWidth > widthMin ? tab.offsetWidth : widthMin
      }

      const newOffset = navWidth - currentOffset > containerWidth * 2
        ? currentOffset + containerWidth - widthMin
        : (navWidth - containerWidth)

      this.setOffset(newOffset)
    },
    getCurrentScrollOffset () {
      const { navStyle } = this
      return navStyle.transform
        ? Number(navStyle.transform.match(/translateX\(-(\d+(\.\d+)*)px\)/)[1])
        : 0
    },
    setOffset (value) {
      this.navStyle.transform = `translateX(-${value}px)`
    },
    scrollToActiveTab () {
      if (!this.scrollable) return
      const nav = this.$refs.nav
      const activeTab = this.$el.querySelector(`.${prefixCls}-tab-active`)
      if (!activeTab) return

      const navScroll = this.$refs.navScroll
      const activeTabBounding = activeTab.getBoundingClientRect()
      const navScrollBounding = navScroll.getBoundingClientRect()
      const navBounding = nav.getBoundingClientRect()
      const currentOffset = this.getCurrentScrollOffset()
      let newOffset = currentOffset

      if (navBounding.right < navScrollBounding.right) {
        newOffset = nav.offsetWidth - navScrollBounding.width
      }

      if (activeTabBounding.left < navScrollBounding.left) {
        newOffset = currentOffset - (navScrollBounding.left - activeTabBounding.left)
      } else if (activeTabBounding.right > navScrollBounding.right) {
        newOffset = currentOffset + activeTabBounding.right - navScrollBounding.right
      }

      if (currentOffset !== newOffset) {
        this.setOffset(Math.max(newOffset, 0))
      }
    },
    updateNavScroll () {
      const navWidth = this.$refs.nav.offsetWidth
      const containerWidth = this.$refs.navScroll.offsetWidth
      const currentOffset = this.getCurrentScrollOffset()
      if (containerWidth < navWidth) {
        this.scrollable = true
        if (navWidth - currentOffset < containerWidth) {
          this.setOffset(navWidth - containerWidth)
        }
      } else {
        this.scrollable = false
        if (currentOffset > 0) {
          this.setOffset(0)
        }
      }
    },
    handleResize () {
      this.updateNavScroll()
    },
    isInsideHiddenElement () {
      let parentNode = this.$el.parentNode
      while (parentNode && parentNode !== document.body) {
        if (parentNode.style && parentNode.style.display === 'none') {
          return parentNode
        }
        parentNode = parentNode.parentNode
      }
      return false
    },
    calHeight () {
      return ''
    }
  },
  mounted () {
    this.showSlot = this.$slots.extra !== undefined
    this.observer = elementResizeDetectorMaker()
    this.observer.listenTo(this.$refs.navWrap, this.handleResize)

    const hiddenParentNode = this.isInsideHiddenElement()
    if (hiddenParentNode) {
      this.mutationObserver = new Artery.MutationObserver(() => {
        if (hiddenParentNode.style.display !== 'none') {
          this.updateBar()
          this.mutationObserver.disconnect()
        }
      })
      this.mutationObserver.observe(hiddenParentNode, {
        attributes: true,
        childList: true,
        characterData: true,
        attributeFilter: ['style']
      })
    }
    if (this.shareframe) {
      this.getTabs().forEach((pane, index) => {
        if (this.activeKey === pane.name || this.activeKey === index) {
          this.currentSrc = pane.src
        }
      })
    }
  },
  beforeDestroy () {
    this.observer.removeListener(this.$refs.navWrap, this.handleResize)
    if (this.mutationObserver) this.mutationObserver.disconnect()
  },
  render (h) {
    const {
      prefixCls,
      barContainnerClasses,
      showSlot,
      scrollable,
      scrollPrev,
      scrollNext,
      navStyle,
      barClasses,
      barStyle,
      activeKey,
      navList,
      handleChange,
      handleRemove,
      contentClasses,
      contentStyle,
      position,
      classes,
      calHeight,
      shareframe
    } = this
    const slotPart = showSlot ? <div class={[`${prefixCls}-nav-right`]}>{this.$slots.extra}</div> : ''
    let contentPart = ''
    if (shareframe) {
      contentPart = <div class={contentClasses} style={[contentStyle, calHeight]}>
        <aty-iframe src={this.currentSrc} class="aty-tabs-frame"></aty-iframe>
        <template v-show={false}>
          {this.$slots.default}
        </template>
      </div>
    } else {
      contentPart = <div class={contentClasses} style={[contentStyle, calHeight]}>
        {this.$slots.default}
      </div>
    }

    const tabPart = (
      <div class={barContainnerClasses} style="calHeight">
        {slotPart}
        <div ref="navWrap"
              class={[`${prefixCls}-nav-wrap`, scrollable ? `${prefixCls}-nav-scrollable` : '', `${prefixCls}-nav-container`]}>
          <Icon type="chevron-left"
                class={[`${prefixCls}-nav-prev`, scrollable ? '' : `${prefixCls}-nav-scroll-disabled`]}
                onClick={scrollPrev}></Icon>
          <Icon type="chevron-right"
                class={[`${prefixCls}-nav-next`, scrollable ? '' : `${prefixCls}-nav-scroll-disabled`]}
                onClick={scrollNext}></Icon>
          <div ref="navScroll" class={[`${prefixCls}-nav-scroll`]}>
            <div ref="nav" class={[`${prefixCls}-nav`, 'nav-text']} style={navStyle}>
              <div class={barClasses} style={barStyle}></div>
              <tab position={this.position} activeKey={activeKey} navList={navList}
                    on-tabchange={handleChange}
                    onRemove={handleRemove}></tab>
            </div>
          </div>
        </div>
      </div>
    )
    if (position === 'bottom') {
      return <div class={classes}>
        {contentPart}
        {tabPart}
      </div>
    } if (position === 'top') {
      return <div class={classes}>
        {tabPart}
        {contentPart}
      </div>
    } else {
      return <div class={classes}>
        {tabPart}
        <div class={[`${prefixCls}-content-wrapper`]}>
          {contentPart}
        </div>
      </div>
    }
  }
}
export default module
